package screen1;

import procedure.Find;
import procedure.Node;
import procedure.TestProcess;

import java.util.*;

/**
 * @author sk.z
 */
public class Screen {

    private List<Point> pointList = new ArrayList<>();
    private List<Line> lineList = new ArrayList<>();

    public Screen(Node process) {
        init(process);
    }

    public static void main(String[] args) {
        Screen screen = new Screen(TestProcess.C());
        screen.print();
    }

    public void print() {
        distributeCreateLine();
        mergePointFineLine();
        showLineList();
    }

    //分流建立输出行
    private void distributeCreateLine() {
        pointList.sort(Comparator.comparingInt(o -> o.deep));

        Line line = new Line();
        line.add(pointList.get(0));
        lineList.add(line);

        for (Point point : pointList) {
            if (point.inLint()) {
                distributeAfterPoint(point);
            }
        }
    }

    //合流寻找输出行
    private void mergePointFineLine() {
        for (Point point : pointList) {
            if (!point.inLint() && point.preList.size() > 1) {
                int size = point.preList.size();
                int mid = midNum(size);
                boolean isEven = isEven(size);
                Point midPoint = point.preList.get(mid);
                if (!midPoint.inLint()) {
                    continue;
                }
                if (isEven) {
                    findPreLine(midPoint.myLine).add(point);
                } else {
                    midPoint.myLine.add(point);
                }
            }
        }
    }


    private void distributeAfterPoint(Point point) {
        if (point.afterList.isEmpty()) {
            return;
        }

        List<Point> afterList = point.afterList;
        int size = afterList.size();
        int mid = midNum(size);
        boolean isEven = isEven(size);
        int afterOffset = 0;

        for (int i = 0; i < size; i++) {
            Point curPoint = afterList.get(i);
            if (curPoint.preList.size() > 1) {
                continue;
            }

            if (i < mid) {
                //插前行
                Line line = insertNewBefore(point.myLine);
                line.add(curPoint);
            } else if (i == mid) {
                if (isEven) {
                    //插下行
                    Line line = insertNewAfter(point.myLine, afterOffset);
                    line.add(afterList.get(i));
                    afterOffset++;
                } else {
                    //插本行
                    point.myLine.add(curPoint);
                }
            } else {
                //插下行
                Line line = insertNewAfter(point.myLine, afterOffset);
                line.add(curPoint);
                afterOffset++;
            }
        }
    }

    private Line findPreLine(Line line) {
        for (int i = 0; i < lineList.size(); i++) {
            if (lineList.get(i) == line) {
                return i == 0 ? line : lineList.get(i - 1);
            }
        }
        throw new IllegalArgumentException("no line exists");
    }

    private Line insertNewBefore(Line line) {
        for (int i = 0; i < lineList.size(); i++) {
            if (lineList.get(i) == line) {
                lineList.add(i, new Line());
                return lineList.get(i);
            }
        }
        throw new IllegalArgumentException("no line exists");
    }

    private Line insertNewAfter(Line line, int offset) {
        for (int i = 0; i < lineList.size(); i++) {
            if (lineList.get(i) == line) {
                i = i + 1 + offset;
                lineList.add(i, new Line());
                return lineList.get(i);
            }
        }
        throw new IllegalArgumentException("no line exists");
    }

    private void showLineList() {
        for (Line l : lineList) {
            l.show();
        }
    }


    private void init(Node process) {
        List<List<Node>> splitList = Find.travelGetAllSingleLine(process);

        //Node -> Point
        Map<Node, Point> nodeMapPoint = new HashMap<>();
        Map<Point, Node> pointMapNode = new HashMap<>();
        for (List<Node> line : splitList) {
            for (int i = 0; i < line.size(); i++) {
                Node n = line.get(i);
                if (nodeMapPoint.containsKey(n)) {
                    Point p = nodeMapPoint.get(n);
                    p.deep = Math.max(p.deep, i);
                    continue;
                }

                Point p = new Point();
                p.name = n.getName();
                p.deep = i;

                nodeMapPoint.put(n, p);
                pointMapNode.put(p, n);
                pointList.add(p);
            }
        }

        //connect
        for (Point p : pointList) {
            Node node = pointMapNode.get(p);
            if (node.getPreList().size() > 0) {
                for (Node n : node.getPreList()) {
                    p.preList.add(nodeMapPoint.get(n));
                }
            }
            if (node.getAfterList().size() > 0) {
                for (Node n : node.getAfterList()) {
                    p.afterList.add(nodeMapPoint.get(n));
                }
            }
        }
    }

    private int midNum(int i) {
        if (i == 0) {
            return 0;
        }
        return i / 2;
    }

    private boolean isEven(int i) {
        return i % 2 == 0;
    }
}
